package org.kymjs.kjframe.demo;


import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;

import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

@SuppressLint("StaticFieldLeak")
public final class UsbManage {

    private static final String ACTION_USB_PERMISSION = "com.coco.hid.USB_PERMISSION";
    public static UsbDeviceConnection connection = null;
    private static Context context = null;
    public static int dataLength = 0;
    public static UsbDevice device = null;
    public static UsbSerialDriver driver = null;
    public static UsbManager manager = null;
    public static UsbSerialPort port = null;
    public static int timeOut = 1000;

    public static final int X1 = 1000;
    public static final int Y1 = 80;
    public static final int X2 = 900;
    public static final int Y2 = 180;

    public static boolean init(Context ctx) {
        context = ctx;
        manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        List<UsbSerialDriver> findAllDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
        if (findAllDrivers.isEmpty()) {
            return false;
        }
        driver = findAllDrivers.get(0);
        device = driver.getDevice();
        requestPermission(device);
        return true;
    }

    public static int clampToInt(double value) {
        if (Double.isNaN(value)) return Integer.MAX_VALUE;
        if (value < 0) return 0;
        if (value > Integer.MAX_VALUE) return Integer.MAX_VALUE;
        return (int) value;
    }

    public static int reverseTargetValue(double y) {
        if (Y1 == Y2) return Integer.MAX_VALUE;
        double result = X1 + (y - Y1) * (X2 - X1) / (Y2 - Y1);
        return clampToInt(result);
    }

    public static class MouseCoordinates {
        public int x;
        public int y;

        public MouseCoordinates(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public static MouseCoordinates calculateMouseCoordinates(double x, double y) {
        int mouseY1 = 4095 * reverseTargetValue(y) / ScreenManager.getDeviceScreenWidth();
        int mouseX1 = (int) (4095 * x / ScreenManager.getDeviceScreenHeight());
        return new MouseCoordinates(mouseX1, mouseY1);
    }

    public static int[] convertToHIDCoordinates(double screenX, double screenY, int hidMaxValue) {
        int hidX = (int) ((screenX * hidMaxValue) / ScreenManager.getDeviceScreenWidth());
        int hidY = (int) ((screenY * hidMaxValue) / ScreenManager.getAutoScreenHeight());
        return new int[]{hidX, hidY};
    }

    public static boolean click(double x, double y) {
        if (ScreenManager.isLandscape()) {
            MouseCoordinates coordinates = calculateMouseCoordinates(x, y);
            sendData(touchReport(3, coordinates.y, coordinates.x));
            sleep(100);
            sendData(touchReport(0, 0, 0));
        } else {
            int[] coords = convertToHIDCoordinates(x, y, 4095);
            sendData(touchReport(3, coords[0], coords[1]));
            sleep(100);
            sendData(touchReport(0, 0, 0));
        }
        return true;
    }

    public static boolean swipe(double x, double y, double ex, double ey) {
        if (ScreenManager.isLandscape()) {
            MouseCoordinates c1 = calculateMouseCoordinates(x, y);
            MouseCoordinates c2 = calculateMouseCoordinates(ex, ey);
            sendData(touchReport(3, c1.y, c1.x));
            sleep(100);
            sendData(touchReport(3, c2.y, c2.x));
            sleep(100);
            sendData(touchReport(0, 0, 0));
        } else {
            int[] start = convertToHIDCoordinates(x, y, 4095);
            int[] end = convertToHIDCoordinates(ex, ey, 4095);
            sendData(touchReport(3, start[0], start[1]));
            sleep(100);
            sendData(touchReport(3, end[0], end[1]));
            sleep(100);
            sendData(touchReport(0, 0, 0));
        }
        return true;
    }

    public static boolean swipe1(double startX, double startY, double endX, double endY, double step, long delay) {
        long stepDelay = (long) (delay / step);
        return performSwipe(startX, startY, endX, endY, (int) step, stepDelay);
    }

    public static boolean performSwipe(double startX, double startY, double endX, double endY, int steps, long stepDelay) {
        double dx = endX - startX;
        double dy = endY - startY;
        double stepX = dx / steps;
        double stepY = dy / steps;
        for (int i = 1; i <= steps; i++) {
            double x = startX + stepX * i;
            double y = startY + stepY * i;
            if (ScreenManager.isLandscape()) {
                MouseCoordinates c = calculateMouseCoordinates(x, y);
                sendData(touchReport(3, c.y, c.x));
            } else {
                int[] hid = convertToHIDCoordinates(x, y, 4095);
                sendData(touchReport(3, hid[0], hid[1]));
            }
            sleep(stepDelay);
        }
        sendData(touchReport(0, 0, 0));
        return true;
    }

    public static boolean release() {
        sendData(touchReport(0, 0, 0));
        return true;
    }

    public static boolean press(double x, double y) {
        if (ScreenManager.isLandscape()) {
            MouseCoordinates c = calculateMouseCoordinates(x, y);
            sendData(touchReport(3, c.y, c.x));
        } else {
            int[] hid = convertToHIDCoordinates(x, y, 4095);
            sendData(touchReport(3, hid[0], hid[1]));
        }
        return true;
    }

    public static boolean connect() {
        connection = manager.openDevice(device);
        if (connection == null) return false;
        port = driver.getPorts().get(0);
        try {
            port.open(connection);
            port.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE);
            Thread.sleep(100);
            if (!port.getDTR()) port.setDTR(true);
            return true;
        } catch (IOException | InterruptedException e) {
            return false;
        }
    }

    public static String getData(int timeout) {
        try {
            if (!port.getDTR()) port.setDTR(true);
            Thread.sleep(50);
            byte[] buf = new byte[64];
            dataLength = port.read(buf, timeout);
            return new String(buf, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    public static byte[] touchReport(int buttons, int x, int y) {
        int xH = (x >> 8) & 0xFF;
        int xL = x & 0xFF;
        int yH = (y >> 8) & 0xFF;
        int yL = y & 0xFF;
        return new byte[]{0x03, (byte) buttons, (byte) xH, (byte) xL, (byte) yH, (byte) yL};
    }

    public static byte[] mouseReport(int buttons, int x, int y) {
        return new byte[]{0x02, (byte) buttons, (byte) x, (byte) y};
    }

    public static byte[] keyReport(int modifiers, List<Integer> keys) {
        byte[] data = new byte[8];
        data[0] = 0x01;
        data[1] = (byte) modifiers;
        data[2] = 0x00;
        for (int i = 0; i < 6 && i < keys.size(); i++) {
            data[3 + i] = keys.get(i).byteValue();
        }
        return data;
    }

    public static String sendData(byte[] data) {
        return sendData(data, timeOut, true);
    }

    public static String sendData(byte[] data, int timeout, boolean toggleDTR) {
        try {
            if (toggleDTR) port.setDTR(true);
            port.write(data, timeout);
            if (toggleDTR) port.setDTR(false);
        } catch (IOException e) {
            return e.getMessage();
        }
        return "";
    }

    public static String sendData(String data) {
        return sendData(data, timeOut, true);
    }

    public static String sendData(String data, int timeout, boolean toggleDTR) {
        try {
            if (toggleDTR) port.setDTR(true);
            port.write((data + "\r\n").getBytes(StandardCharsets.UTF_8), timeout);
            if (toggleDTR) port.setDTR(false);
        } catch (IOException e) {
            return e.getMessage();
        }
        return "";
    }

    public static boolean isOpen() {
        return port != null && port.isOpen();
    }

    public static UsbDevice getUsbDevice() {
        Map<String, UsbDevice> deviceList = manager.getDeviceList();
        if (deviceList.isEmpty()) return null;
        return deviceList.values().iterator().next();
    }

    public static void requestPermission(UsbDevice usbDevice) {
        PendingIntent pi = PendingIntent.getBroadcast(context, 0,
                new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
        manager.requestPermission(usbDevice, pi);
    }

    private static void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException ignored) {
        }
    }
}

